package org.yzh.web.endpoint;

import com.alibaba.fastjson.JSON;
import io.github.yezhihao.netmc.core.annotation.AsyncBatch;
import io.github.yezhihao.netmc.core.annotation.Endpoint;
import io.github.yezhihao.netmc.core.annotation.Mapping;
import io.github.yezhihao.netmc.session.Session;
import io.github.yezhihao.netmc.util.AdapterList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;
import org.yzh.protocol.basics.JTMessage;
import org.yzh.protocol.t808.*;
import org.yzh.web.commons.DateUtils;
import org.yzh.web.commons.EncryptUtils;
import org.yzh.web.component.handler.NettyWebSocketHandler;
import org.yzh.web.mapper.mg.service.T0200Service;
import org.yzh.web.model.enums.SessionKey;
import org.yzh.web.model.po.MongoT0200PO;
import org.yzh.web.model.vo.DeviceInfo;
import org.yzh.web.service.DeviceService;
import org.yzh.web.service.FileService;
import org.yzh.web.service.LocationService;

import javax.annotation.Resource;
import java.util.*;

import static org.yzh.protocol.commons.JT808.*;

@Endpoint
@Component
public class JT808Endpoint {

    private static final Logger log = LoggerFactory.getLogger(JT808Endpoint.class.getSimpleName());

    @Autowired
    private LocationService locationService;

    @Autowired
    private DeviceService deviceService;

    @Autowired
    private FileService fileService;

    @Autowired
    private T0200Service t0200Service;


    @Resource
    private MongoTemplate mongoTemplate;


    @Mapping(types = 终端通用应答, desc = "终端通用应答")
    public Object generalResponse(T0001 message, Session session) {
        session.response(message);
        return null;
    }

    @Mapping(types = 终端心跳, desc = "终端心跳")
    public void heartbeat(JTMessage message, Session session) {
        Map m = new HashMap ();
        m.put ("clientId",message.getClientId ());
        m.put("sessions",JTSessionListener.DVS);
        NettyWebSocketHandler.sendJsonMsg (m);
    }

    @Mapping(types = 终端注销, desc = "终端注销")
    public void unregister(JTMessage message, Session session) {
        session.invalidate();
    }

    @Mapping(types = 查询服务器时间, desc = "查询服务器时间")
    public T8004 serverTime(JTMessage message, Session session) {
        T8004 result = new T8004(DateUtils.yyMMddHHmmss.format(new Date(System.currentTimeMillis() + 50)));
        return result;
    }

    @Mapping(types = 终端补传分包请求, desc = "终端补传分包请求")
    public void retransmissionPacket(T8003 message, Session session) {
    }

    @Mapping(types = 终端注册, desc = "终端注册")
    public T8100 register(T0100 message, Session session) {
        if (message.getPlateNo() == null) {
            session.setOfflineCache(message.getClientId(), -1);
            session.setAttribute(SessionKey.ProtocolVersion, -1);
            log.warn(">>>>>>>>>>可能为2011版本协议，将在下次请求时尝试解析{},{}", session, message);
            return null;
        } else {
            session.setAttribute(SessionKey.ProtocolVersion, message.getVersionNo());
        }

        T8100 result = new T8100();
        result.setResponseSerialNo(message.getSerialNo());

        DeviceInfo device = deviceService.register(message);
        if (device != null) {
            session.register(message);
            session.setAttribute(SessionKey.DeviceInfo, device);

            byte[] bytes = DeviceInfo.toBytes(device);
            bytes = EncryptUtils.encrypt(bytes);
            String token = Base64.getEncoder().encodeToString(bytes);

            result.setToken(token);
            result.setResultCode(T8100.Success);
        } else {
            result.setResultCode(T8100.NotFoundTerminal);
        }
        return result;
    }

    @Mapping(types = 终端鉴权, desc = "终端鉴权")
    public T0001 authentication(T0102 message, Session session) {
        T0001 result = new T0001();
        result.setResponseSerialNo(message.getSerialNo());
        result.setResponseMessageId(message.getMessageId());

        DeviceInfo device = deviceService.authentication(message);
        if (device != null) {
            session.register(message);
            session.setAttribute(SessionKey.DeviceInfo, device);
            result.setResultCode(T0001.Success);
            return result;
        }
        log.warn("终端鉴权失败，{}{}", session, message);
        result.setResultCode(T0001.Failure);
        return result;
    }

    @Mapping(types = 查询终端参数应答, desc = "查询终端参数应答")
    public void parametersResponse(T0104 message, Session session) {
        session.response(message);
    }

    @Mapping(types = 查询终端属性应答, desc = "查询终端属性应答")
    public void attributesResponse(T0107 message, Session session) {
        session.response(message);
    }

    @Mapping(types = 终端升级结果通知, desc = "终端升级结果通知")
    public void upgradeResponse(T0108 message, Session session) {
    }

    /**
     * 异步批量处理
     * poolSize：参考数据库CPU核心数量
     * maxElements：最大累积4000条记录处理一次
     * maxWait：最大等待时间1秒
     */
    @AsyncBatch(poolSize = 2, maxElements = 4000, maxWait = 1000)
    @Mapping(types = 位置信息汇报, desc = "位置信息汇报")
        public void locationReport(List<T0200> list) {
        System.out.println("进入位置汇报");
        log.info ("数据:{}",JSON.toJSONString (list));
        //存入mongodb
        Map<String,Object> results = new HashMap<>();
        list.stream ().forEach (t0200 -> {
            MongoT0200PO mongoT0200PO = new MongoT0200PO();
            results.put("deviceId",t0200.getClientId());
            results.put("lng",t0200.getLat());
            results.put("lat",t0200.getLng());
            System.out.println(t0200);
            BeanUtils.copyProperties(t0200,mongoT0200PO);
            //存入mongodb
            mongoTemplate.insert(mongoT0200PO,"jt-location");

//            Query query = new Query();
//            query.addCriteria(Criteria.where("clientId").is("10329896069"));
//            DeleteResult deleteResult = mongoTemplate.remove(query,MongoT0200PO.class);
//            System.out.println(deleteResult.getDeletedCount() + "deleteCount");
            //updateMulti 如果根据查询条件找到对应的多条记录是，全部更新
            //updateFirst 更改符合条件的第一个
            //upsert 顾名思义 update+insert 如果根据条件没有对应的数据,则执行插入
            //findAndModify 查询然后更新

//            mongoTemplate.update();
//            mongoTemplate.updateFirst();
//            mongoTemplate.updateMulti();
//            mongoTemplate.findAndModify();
//            mongoTemplate.upsert();
        });
        //发送socket
        NettyWebSocketHandler.sendJsonMsg(results);
        //入库
        locationService.batchInsert(list);
        log.info ("位置信息汇报结束");
    }

    @Mapping(types = 定位数据批量上传, desc = "定位数据批量上传")
    public void locationBatchReport(T0704 message) {
        System.out.println("进入定位数据批量上传");
        log.info ("数据:{}",JSON.toJSONString (message));
        Session session = message.getSession();
        List<T0200> list = new AdapterList<>(message.getItems(), item -> {
            T0200 location = item.getLocation();
            location.copyBy(message);
            location.setSession(session);
            location.transform();
            return location;
        });
        //存入mongodb
        list.stream ().forEach (t0200 -> {
            mongoTemplate.insert (JSON.toJSONString (t0200),"jt-location");
        });
        //发送socket
        //NettyWebSocketHandler.sendJsonMsg (list);
        locationService.batchInsert(list);
        log.info ("定位数据批量上传结束");
    }

    @Mapping(types = {位置信息查询应答, 车辆控制应答}, desc = "位置信息查询应答/车辆控制应答")
    public void locationResponse(T0201_0500 message, Session session) {
        session.response(message);
    }

    @Mapping(types = 事件报告, desc = "事件报告")
    public void eventReport(T0301 message, Session session) {
    }

    @Mapping(types = 提问应答, desc = "提问应答")
    public void questionResponse(T0302 message, Session session) {
    }

    @Mapping(types = 信息点播_取消, desc = "信息点播/取消")
    public void newsCancel(T0303 message, Session session) {
    }

    @Mapping(types = 查询区域或线路数据应答, desc = "查询区域或线路数据应答")
    public void areaLocationResponse(T0608 message, Session session) {
        session.response(message);
    }

    @Mapping(types = 行驶记录数据上传, desc = "行驶记录仪数据上传")
    public void driveRecorderResponse(T0700 message, Session session) {
        session.response(message);
    }

    @Mapping(types = 电子运单上报, desc = "电子运单上报")
    public void ewaybillReport(JTMessage message, Session session) {
    }

    @Mapping(types = 驾驶员身份信息采集上报, desc = "驾驶员身份信息采集上报")
    public void driverInfoResponse(T0702 message, Session session) {
        session.response(message);
    }

    @Mapping(types = CAN总线数据上传, desc = "CAN总线数据上传")
    public void canBusDataReport(T0705 message, Session session) {
    }

    @Mapping(types = 多媒体事件信息上传, desc = "多媒体事件信息上传")
    public void mediaEventReport(T0800 message, Session session) {
    }

    @Mapping(types = 多媒体数据上传, desc = "多媒体数据上传")
    public T8800 mediaUploadResponse(T0801 message, Session session) {
        fileService.saveMediaFile(message);
        T8800 result = new T8800();
        result.setMediaId(message.getId());
        return result;
    }

    @Mapping(types = 存储多媒体数据检索应答, desc = "存储多媒体数据检索应答")
    public void mediaSearchResponse(T0802 message, Session session) {
        session.response(message);
    }

    @Mapping(types = 摄像头立即拍摄命令应答, desc = "摄像头立即拍摄命令应答")
    public void mediaVideoRecordResponse(T0805 message, Session session) {
        session.response(message);
    }

    @Mapping(types = 数据上行透传, desc = "数据上行透传")
    public void passthrough(T0900 message, Session session) {
    }

    @Mapping(types = 数据压缩上报, desc = "数据压缩上报")
    public void gzipPackReport(T0901 message, Session session) {
    }

    @Mapping(types = 终端RSA公钥, desc = "终端RSA公钥")
    public void rsaSwap(T0A00_8A00 message, Session session) {
        session.response(message);
    }
}